package com.welovecoding.towerdefense;

import java.util.ArrayList;
import java.util.Random;
import java.util.Vector;

import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.BoundCamera;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.engine.camera.hud.HUD;
import org.anddev.andengine.engine.handler.IUpdateHandler;
import org.anddev.andengine.engine.handler.timer.ITimerCallback;
import org.anddev.andengine.engine.handler.timer.TimerHandler;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.IEntity;
import org.anddev.andengine.entity.modifier.IEntityModifier.IEntityModifierListener;
import org.anddev.andengine.entity.modifier.MoveByModifier;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnAreaTouchListener;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.scene.Scene.ITouchArea;
import org.anddev.andengine.entity.shape.IShape;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.entity.text.ChangeableText;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.font.Font;
import org.anddev.andengine.opengl.font.FontFactory;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.ui.activity.BaseGameActivity;
import org.anddev.andengine.util.modifier.IModifier;

import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

public class TowerDefenseActivity extends BaseGameActivity implements
		IOnSceneTouchListener, IEntityModifierListener, IOnAreaTouchListener {
	// I am Main class//

	// ========================================
	// Create Camera and Scenes
	// =======================================
	int CAMERA_WIDTH = 720;
	int CAMERA_HEIGHT = 480;
	Camera camera;
	Scene scene;
	BoundCamera mBoundChaseCamera;

	// ========================================
	// Tower in Arrays
	// ========================================
	BitmapTextureAtlas towerImage;
	TextureRegion towerTexture;
	Vector<Tower> arrayTower;
	Tower tw; // this is Tower class and its only used when creating towers on
				// touch event

	// ========================================
	// The Bullet in Array
	// ========================================
	BitmapTextureAtlas bulletImage;
	TextureRegion bulletTexture;
	Vector<Sprite> arrayBullet;

	// ========================================
	// The Enemy and Array
	// ========================================
	BitmapTextureAtlas enImage;
	TiledTextureRegion enTexture;
	Enemy Enemy;
	Vector<Enemy> arrayEn;

	// ========================================
	// The Bomb and Array
	// ========================================
	BitmapTextureAtlas bombImage;
	TiledTextureRegion bombTexture;
	Bomb bomb;
	Vector<Bomb> arrayBomb;
	int bomb_delay = 50;

	// ========================================
	// The HUD ==> Score, Life, etc
	// ========================================
	long score = 0;
	int intLife = 100;
	ChangeableText scoreText, lifeText;
	ProgressBar life;

	// ========================================
	// Others
	// ========================================
	// for touches
	float touchX;
	float touchY;

	// Enemy location // updated real time in a loop
	float targetX;
	float targetY;

	Handler TIMER_ONE; // our thread

	// ==== just for testing outputs ====// never used
	BitmapTextureAtlas fontTexture;
	Font font;
	ChangeableText fpsFont;

	@Override
	public void onLoadResources() {
		// =================================================================================//
		// Load Towers
		// ================================================================================//
		// ==== Tower Type 1
		towerImage = new BitmapTextureAtlas(512, 512);
		towerTexture = BitmapTextureAtlasTextureRegionFactory.createFromAsset(
				this.towerImage, this, "gfx/tower.png", 0, 0);
		mEngine.getTextureManager().loadTextures(towerImage);

		// =================================================================================//
		// Load Bullets
		// ================================================================================//
		// ==== Bullet Type 1
		bulletImage = new BitmapTextureAtlas(512, 512);
		bulletTexture = BitmapTextureAtlasTextureRegionFactory.createFromAsset(
				this.bulletImage, this, "gfx/bullet.png", 0, 0);
		mEngine.getTextureManager().loadTextures(bulletImage);

		// =================================================================================//
		// Load Enemy
		// ================================================================================//
		// ==== Enemy Type 1
		enImage = new BitmapTextureAtlas(512, 512);
		enTexture = BitmapTextureAtlasTextureRegionFactory
				.createTiledFromAsset(this.enImage, this, "gfx/enemy1.png", 0,
						0, 2, 1);
		mEngine.getTextureManager().loadTextures(enImage);

		// =================================================================================//
		// Load Bomb
		// ================================================================================//
		bombImage = new BitmapTextureAtlas(512, 512);
		bombTexture = BitmapTextureAtlasTextureRegionFactory
				.createTiledFromAsset(this.bombImage, this, "gfx/bullet.png",
						0, 0, 1, 1);
		mEngine.getTextureManager().loadTexture(bombImage);

		// ==== for text
		fontTexture = new BitmapTextureAtlas(256, 256,
				TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		font = FontFactory.create(fontTexture, Typeface.DEFAULT, 24, true,
				Color.RED);
		mEngine.getTextureManager().loadTexture(fontTexture);
		mEngine.getFontManager().loadFont(font);
	}

	@Override
	public void onLoadComplete() {

	}

	@Override
	public Engine onLoadEngine() {
		camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		return mEngine = new Engine(new EngineOptions(true,
				ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(
						CAMERA_WIDTH, CAMERA_HEIGHT), camera).setNeedsMusic(
				true).setNeedsSound(true));
	}

	@Override
	public Scene onLoadScene() {
		scene = new Scene();

		// =====================================
		// Define Array
		// =====================================
		arrayTower = new Vector<Tower>();
		// arrayBullet = new ArrayList<Sprite>(); //useless // we have array of
		// bullets in Tower class
		arrayEn = new Vector<Enemy>();
		arrayBomb = new Vector<Bomb>();
		arrayEn.clear();
		arrayBomb.clear();

		scene.registerUpdateHandler(loop);
		scene.setTouchAreaBindingEnabled(true);
		scene.setOnSceneTouchListener(this);

		scoreText = new ChangeableText(500, 5, this.font, "Score:",
				"Score: XXXXXX".length());
		scoreText.setText("Score:      0");

		lifeText = new ChangeableText(300, 5, this.font, "Life:",
				"Life: XXX".length());
		lifeText.setText("Life: 100");
		// scene.attachChild(scoreText);

		life = new ProgressBar(this.camera, 10, 5, 250, 20);
		life.attachChild(scoreText);
		life.attachChild(lifeText);
		// life.setFrameColor(0.0f, 0.0f, 0.0f, 0.0f);
		life.setBackColor(1.0f, 0.0f, 0.0f, 0.0f);
		life.setProgressColor(0.0f, 1.0f, 0.0f, 0.0f);
		life.setProgress(50);

		Sprite dispTower = new Sprite(10, 300, 100, 100, towerTexture);
		life.attachChild(dispTower);
		dispTower.setUserData(new Integer(1));
		camera.setHUD(life);

		add_enemy(); // timer add enemy every amount of defined secs

		TIMER_ONE = new Handler(); // Thread Created
		TIMER_ONE_START(); // start our Thread

		scene.setOnAreaTouchListener(this);
		scene.registerTouchArea(dispTower);

		return scene;
	}

	@Override
	public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
			ITouchArea pTouchArea, float pTouchAreaLocalX,
			float pTouchAreaLocalY) {
		int ID = 0;
		try {
			ID = (Integer) ((IShape) pTouchArea).getUserData();
		} catch (ClassCastException cce) {
		}
		switch (ID) {
		case 1: {
			// Toast.makeText(getApplicationContext(), "buat tower",
			// Toast.LENGTH_SHORT).show();
			touchX = pSceneTouchEvent.getX();
			touchY = pSceneTouchEvent.getY();
			tw = new Tower(bulletTexture, touchX, touchY - 50, 50, 50,
					towerTexture) {
				public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
						float pTouchAreaLocalX, float pTouchAreaLocalY) {
					tw.setPosition(pSceneTouchEvent.getX() - this.getWidth()
							/ 2, pSceneTouchEvent.getY() - this.getHeight() / 2);
					return true;
				}
			};
			arrayTower.add(tw); // add to array
			scene.registerTouchArea(tw); // register touch area , so this allows
											// you to drag it
			scene.attachChild(tw); // add it to the scene
			break;
		}
		}
		return false;
	}

	IUpdateHandler loop = new IUpdateHandler() {
		@Override
		public void reset() {
		}

		@Override
		public void onUpdate(float pSecondsElapsed) {
			// =================Code must go here=======================
			// this is free to do any other things you like!
			// code ends
			collision();
		}
	};

	int allow_enemy;

	public void add_enemy() {
		final float delay = 2f;
		TimerHandler enemy_handler = new TimerHandler(delay, true,
				new ITimerCallback() {

					@Override
					public void onTimePassed(TimerHandler pTimerHandler) {
						// =================Code must go
						// here=======================
						allow_enemy++;
						Random a = new Random();
						int y = a.nextInt(CAMERA_HEIGHT - 100);

						if (allow_enemy <= 2) {
							Enemy = new Enemy(CAMERA_WIDTH, y, enTexture);
							Enemy.animate(500);
							scene.attachChild(Enemy);
							arrayEn.add(Enemy);
						}
						// this above code adds enemy every 2s
						// ================= end of
						// code==========================
					}
				});
		getEngine().registerUpdateHandler(enemy_handler);

	}

	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {

		if (pSceneTouchEvent.isActionDown()) {
			touchX = pSceneTouchEvent.getX();
			touchY = pSceneTouchEvent.getY();

			if (arrayBomb.size() <= 10 && bomb_delay <= 0) {
				Bomb b = new Bomb(touchX, 0, bombTexture);
				MoveByModifier movByMod = new MoveByModifier(1.5f, 0, touchY,
						this);
				b.registerEntityModifier(movByMod);
				arrayBomb.add(b);
				scene.attachChild(b);
				bomb_delay = 50;
			}

			return true;
		}

		if (pSceneTouchEvent.isActionUp()) {
			return true;
		}

		if (pSceneTouchEvent.isActionMove()) {
			touchX = pSceneTouchEvent.getX();
			touchY = pSceneTouchEvent.getY();
			return true;
		}

		return false;
	}

	public void TIMER_ONE_START() {

		// == THREAD STARTS
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(5);
						TIMER_ONE.post(new Runnable() {

							@Override
							public void run() {
								// ======= BEGIN WRITING
								// ========================================================================
								bomb_delay -= 1;
								collision(); // run the <--collision every 5ms
								// == END WRITING
								// ================================================================================

							} /* Code finishes */
						});
					} catch (Exception e) {
					}
				}
			}
		}).start(); /* Re-start the thread */
		// == THREAD ENDS
	}

	public void collision() {

		int enemySize = arrayEn.size();
		for (int j = 0; j < enemySize; j++) {
			try {
				Enemy enemy = arrayEn.get(j);
				float newPosition = enemy.getX() - 2 / 6f;
				if (newPosition >= 200)
					enemy.setPosition(newPosition, enemy.getY());
				else {
					enemy.changeSpeed(enemy.getSpeed() - 1);
					if (enemy.getSpeed() == 0) {
						enemy.changeSpeed(100);
						decreaseLife();
					}
				}

				// * Lets Loop our Towers
				// * for(Tower tower: arrayTower){
				for (int k = 0; k < arrayTower.size(); k++) {
					Tower tower = (Tower) arrayTower.get(k);
					tower.speed--;
					Enemy targetEnemy = arrayEn.get(0);
					// check if they collide
					if (tower.speed <= 0) {
						fire(tower, targetEnemy);// call the fire and send pass
													// the
						// tower and enemy to fire
						break; // take a break
					}
				}

				int bombSize = arrayBomb.size();
				for (int k = 0; k < bombSize; k++) {
					Bomb b = arrayBomb.get(k);
					if (b.collidesWith(enemy)) {
						enemy.getHit();
						scene.detachChild(b);
						arrayBomb.remove(b);
						if (enemy.getLife() <= 0) {
							// Toast.makeText(getApplicationContext(),
							// "Life : "+enemy.getLife(),
							// Toast.LENGTH_SHORT).show();
							enemy.die();
							// scene.detachChild(enemy);
							// arrayEn.remove(enemy);
							addScore();
						}
					} else if (b.getStatus()) {
						scene.detachChild(b);
						arrayBomb.remove(b);
						// break;
					}
				}
				if (enemy.LifeOrDie()) {
					scene.detachChild(enemy);
					arrayEn.remove(enemy);
					break;
				}

				if (arrayEn.size() <= 0) {
					int bombSize2 = arrayBomb.size();
					for (int k = 0; k < bombSize2; k++) {
						Bomb b = arrayBomb.get(k);
						if (b.getStatus()) {
							scene.detachChild(b);
							arrayBomb.remove(b);
						}
					}
				}
			} catch (Exception ioex) {
				Log.d(getLocalClassName(), CLIPBOARD_SERVICE);
				break;
			}
		}
	}

	public void fire(Tower tower, Enemy enemy) {

		targetX = enemy.getX() + enemy.getWidth() / 2; // simple get the enemy
														// x,y and middle it and
														// tell the bullet where
														// to aim and fire
		targetY = enemy.getY() + enemy.getHeight() / 2;

		ArrayList<Sprite> getBullet = tower.getArrayList(); // gets bullets from
															// Tower class were
															// are bullets are
															// fired from
		tower.fire(targetX, targetY, tower.getX() + tower.getWidth() / 2,
				tower.getY() + tower.getHeight() / 2); // Asks the tower to open
														// fire and places the
														// bullet in middle of
														// tower
		// arrayBullet.add(tt.getBulletSprite());
		scene.attachChild(tower.getBulletSprite());

		// ArrayList<Sprite> a = tt.getArrayList();

		// for(Sprite bullet : getBullet){
		/*
		 * for (int i = 0; i < getBullet.size(); i++) { Sprite bullet =
		 * getBullet.get(i); if(bullet.getX() == targetX && bullet.getY() ==
		 * targetY){ scene.detachChild(bullet); // no longer needed to be shown
		 * getBullet.remove(bullet); // also remove it from array so we // don't
		 * check it again } if (bullet.collidesWith(enemy)) {
		 * scene.detachChild(bullet); // no longer needed to be shown
		 * getBullet.remove(bullet); // also remove it from array so we // don't
		 * check it again enemy.getHit(); // you can remove shoot enemies here
		 * or create enemy class with // its own life here if (enemy.getLife()
		 * <= 0) { enemy.die(); addScore(); scene.detachChild(enemy);
		 * arrayEn.remove(enemy); } //Toast.makeText(getApplicationContext(), //
		 * "Life : " + enemy.getLife(), Toast.LENGTH_SHORT).show(); break; //
		 * take a break } }
		 */
	}

	@Override
	public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {

	}

	@Override
	public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
		// CREATE PARTICLE
		// Bomb bomb = (Bomb) pItem;
		// bomb.isHitTarget();
		// pItem = bomb;

		((Bomb) pItem).isHitTarget();
	}

	private void addScore() {
		this.score += 10;
		if (score >= 10 && score < 100) {
			scoreText.setText("Score:     " + score);
		} else if (score >= 100 && score < 1000) {
			scoreText.setText("Score:    " + score);
		} else if (score >= 1000 && score < 10000) {
			scoreText.setText("Score:   " + score);
		} else if (score >= 10000 && score < 100000) {
			scoreText.setText("Score:  " + score);
		} else if (score >= 100000 && score < 1000000) {
			scoreText.setText("Score: " + score);
		}
	}

	private void decreaseLife() {
		this.intLife -= 10;
		lifeText.setText("Life: " + intLife);
		if (intLife <= 0) {
			// Pokoknya nanti keluar tulisan game over atau gimana
			// this.finish();
		}
	}
}
